package com.hs.sentinelr.config;

import cn.hutool.core.util.StrUtil;
import com.hs.sentinelr.config.properties.RedisProperties;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisNode;
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.net.UnknownHostException;
import java.time.Duration;
import java.util.List;
import java.util.stream.Collectors;

/**
 * redis-sentinel配置相关
 * 使用lettuce
 *
 * @Author lingnan
 * @Date 2021/3/26 5:47 下午
 **/
@Slf4j
@Configuration
@EnableConfigurationProperties(RedisProperties.class)
@ConditionalOnClass(LettuceConnectionFactory.class)
@ConditionalOnExpression("'${spring.redis.mode}'=='single' or '${spring.redis.mode}'=='cluster' or '${spring.redis.mode}'=='sentinel'")
public class RedisTemplateConfig {

    @Autowired
    private RedisProperties redisProperties;


    @Bean
    public RedisTemplate<Object, Object> redisTemplate(@Qualifier("autoLettuceConnectionFactory") RedisConnectionFactory connectionFactory) {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        // 配置连接工厂
        template.setConnectionFactory(connectionFactory);
        //序列化相关
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
        //设置序列化器
        template.setKeySerializer(stringRedisSerializer);
        template.setValueSerializer(genericJackson2JsonRedisSerializer);
        template.setHashKeySerializer(stringRedisSerializer);
        template.setHashValueSerializer(genericJackson2JsonRedisSerializer);
        template.afterPropertiesSet();

        return template;
    }

    @Bean
    public StringRedisTemplate stringRedisTemplate(@Qualifier("autoLettuceConnectionFactory")RedisConnectionFactory redisConnectionFactory){
        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

    @Bean("autoLettuceConnectionFactory")
    public LettuceConnectionFactory autoLettuceConnectionFactory(@Qualifier("autoRedisConfiguration") RedisConfiguration redisConfiguration) {
        LettuceClientConfiguration lettuceClientConfiguration = getDefaultLettuceClientSetting();
        //根据配置和客户端配置创建连接
        LettuceConnectionFactory lettuceConnectionFactory = new
                LettuceConnectionFactory(redisConfiguration, lettuceClientConfiguration);
        //lettuceConnectionFactory.afterPropertiesSet();
        return lettuceConnectionFactory;
    }

    @Bean("autoRedisConfiguration")
    @ConditionalOnProperty(name = "spring.redis.mode", havingValue = "single")
    public RedisConfiguration singleRedisConfiguration() {
        //redis配置
        String[] address = redisProperties.getSingle().getAddress().split(StrUtil.COLON);
        RedisStandaloneConfiguration config = new RedisStandaloneConfiguration(address[0], Integer.valueOf(address[1]));
        config.setDatabase(redisProperties.getDatabase());
        if (StrUtil.isNotBlank(redisProperties.getPassword())) {
            config.setPassword(redisProperties.getPassword());
        }
        return config;
    }

    @Bean("autoRedisConfiguration")
    @ConditionalOnProperty(name = "spring.redis.mode", havingValue = "cluster")
    public RedisConfiguration clusterRedisConfiguration() {
        RedisClusterConfiguration config = new RedisClusterConfiguration();
        List<RedisNode> nodes = redisProperties.getCluster().getNodes()
                .stream()
                .map(n -> {
                    String[] hostPort = n.split(StrUtil.COLON);
                    return new RedisNode(hostPort[0], Integer.valueOf(hostPort[1]));
                }).collect(Collectors.toList());
        config.setClusterNodes(nodes);
        config.setMaxRedirects(redisProperties.getCluster().getMaxRedirects());
        if (StrUtil.isNotBlank(redisProperties.getPassword())) {
            config.setPassword(redisProperties.getPassword());
        }
        return config;
    }

    @Bean("autoRedisConfiguration")
    @ConditionalOnProperty(name = "spring.redis.mode", havingValue = "sentinel")
    public RedisConfiguration sentinelRedisConfiguration() {
        RedisSentinelConfiguration config = new RedisSentinelConfiguration();
        List<RedisNode> nodes = redisProperties.getSentinel().getNodes()
                .stream()
                .map(n -> {
                    String[] hostPort = n.split(StrUtil.COLON);
                    return new RedisNode(hostPort[0], Integer.valueOf(hostPort[1]));
                }).collect(Collectors.toList());
        config.setSentinels(nodes);
        config.setMaster(redisProperties.getSentinel().getMaster());
        config.setDatabase(redisProperties.getDatabase());
        if (StrUtil.isNotBlank(redisProperties.getPassword())) {
            config.setPassword(redisProperties.getPassword());
        }
        return config;
    }


    /**
     * 池配置
     *
     * @return
     */
    private LettuceClientConfiguration getDefaultLettuceClientSetting() {
        //连接池配置
        GenericObjectPoolConfig genericObjectPoolConfig =
                new GenericObjectPoolConfig();
        genericObjectPoolConfig.setMaxIdle(redisProperties.getPool().getMaxIdle());
        genericObjectPoolConfig.setMinIdle(redisProperties.getPool().getMinIdle());
        genericObjectPoolConfig.setMaxTotal(redisProperties.getPool().getMaxActive());
        genericObjectPoolConfig.setMaxWaitMillis(redisProperties.getPool().getMaxWait());

        //redis客户端配置
        LettucePoolingClientConfiguration.LettucePoolingClientConfigurationBuilder
                builder = LettucePoolingClientConfiguration.builder().
                commandTimeout(Duration.ofMillis(redisProperties.getPool().getConnTimeout()));
        builder.shutdownTimeout(Duration.ofMillis(redisProperties.getPool().getStTimeout()));
        builder.poolConfig(genericObjectPoolConfig);
        LettuceClientConfiguration lettuceClientConfiguration = builder.build();
        return lettuceClientConfiguration;
    }

}
